package third.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import fun.codedesign.yinxue.util.CollectionUtil;

/**
 * 采用策略模式用于将jedis返回资源池 <br>
 *
 * @author zengjian
 * @create 2018-05-17 16:36
 * @since 1.0.0
 */
public class RedisClient {

    private JedisPool jedisPool;

    public RedisClient() {
        // 如果是本地redis，给默认的jdesiPool 端口6379
        this(new JedisPool());
    }

    public RedisClient(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 是否存在该key
     *
     * @param key
     * @return 存在返回TRUE 不存在false
     */
    public Boolean exists(final String key) {
        return execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doAction(Jedis jedis) {
                return jedis.exists(key);
            }
        });
    }

    /**
     * 设置key过期时间
     *
     * @param key
     * @param seconds
     * @return 成功返回1 失败返回0
     */
    public Long expire(final String key, final int seconds) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.expire(key, seconds);
            }
        });
    }

    /**
     * 删除key <br>
     *
     * @param key
     * @return 返回被删除的key数量
     */
    public Long del(final String key) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.del(key);
            }
        });
    }

    /**
     * 返回key的值，没有返回null，不是String类型抛error <br>
     *
     * @param key
     * @return value或者null
     */
    public String get(final String key) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }

    /**
     * 设置 key value，会覆盖旧值，同map.put
     *
     * @param key
     * @param value
     * @return 成功返回 "OK" 字符串
     */
    public String set(final String key, final String value) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }

    /**
     * 覆盖旧值，并且设置过期时间 <br>
     *
     * @param key
     * @param seconds
     * @param value
     * @return 设置成功返回 "OK"
     */
    public String setex(final String key, final int seconds, final String value) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.setex(key, seconds, value);
            }
        });
    }

    /**
     * hash设置key下的field value值 <br>
     *
     * @param key
     * @param field
     * @param value
     * @return 如果是新创建并且设置成功，返回1，如果是已存在，覆盖旧值，返回0
     */
    public Long hset(final String key, final String field, final String value) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.hset(key, field, value);
            }
        });
    }

    /**
     * 创建一个新的Map存储或者覆盖旧的 <br>
     *
     * @param key
     * @param map
     * @return 成功返回"OK"
     */
    public String hmset(final String key, final Map<String, String> map) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.hmset(key, map);
            }
        });
    }

    /**
     * 返回hash表中指定字段，如果没有返回null <br>
     *
     * @param key
     * @param field
     * @return
     */
    public String hget(final String key, final String field) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.hget(key, field);
            }
        });
    }

    /**
     * 删除指定哈希中的字段 <br>
     *
     * @param key
     * @param fields
     * @return 被成功删除的field的数量
     */
    public Long hdel(final String key, final String... fields) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.hdel(key, fields);
            }
        });
    }

    /**
     * 哈希中是否存在指定字段 <br>
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean hexists(final String key, final String field) {
        return execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doAction(Jedis jedis) {
                return jedis.hexists(key, field);
            }
        });
    }

    /**
     * 将一个或多个值 value 插入到列表 key 的表头 时间复杂度O(1) <br>
     * 没有会自己创建列表，如果存在的key不是列表类型会返回一个错误 <br>
     *
     * @param key 缓存key
     * @param value []
     * @return Long 列表长度
     */
    public Long lpush(final String key, final String... values) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.lpush(key, values);
            }
        });
    }

    /**
     * 获取指定key的hash <br>
     *
     * @param key
     * @return
     */
    public Map<String, String> hgetAll(final String key) {
        return execute(new RedisCallback<Map<String, String>>() {
            @Override
            public Map<String, String> doAction(Jedis jedis) {
                return jedis.hgetAll(key);
            }
        });
    }

    /**
     * 返回指定key hash的values列表，不存在时 //TODO 返回空list <br>
     *
     * @param key
     * @return
     */
    public List<String> hvalues(final String key) {
        return execute(new RedisCallback<List<String>>() {
            @Override
            public List<String> doAction(Jedis jedis) {
                return jedis.hvals(key);
            }
        });
    }

    /**
     * 指定的不存在的key设置值，设置成功为true，不成功为false <br>
     * jedis.set(key,value,NX,PX,expire) 成功返回的是OK <br>
     *
     * @param key 缓存key
     * @param value 缓存值
     * @param expire 过期时间:单位毫秒
     * @return 是否设置成功
     */
    public boolean setnx(final String key, final String value, final long expire) {
        try {
            return execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doAction(Jedis jedis) {
                    // todo api change
//                    return null != jedis.set(key, value, "NX", "PX", expire);
                    return null;
                }
            });
        } catch (JedisConnectionException jce) {
            // log.error(jce.getMessage(), jce);
            return false;
        }
    }

    /**
     * 功能描述: 在同一个事务中，将数据存入Hash缓存，同时设置缓存失效时间<br>
     *
     * @param key 缓存的key
     * @param datas 缓存的Map
     * @param seconds 缓存失效时间
     */
    public boolean hsetDataAndExpire(final String key, final Map<String, String> datas, final int seconds) {
        try {
            return execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doAction(Jedis jedis) {
                    Transaction tx = jedis.multi();
                    tx.hmset(key, datas);
                    tx.expire(key, seconds);
                    List<Object> exec = tx.exec();
                    return CollectionUtil.isNotEmpty(exec);
                }
            });
        } catch (Exception ex) {
//            log.error(ex.getMessage(), ex);
            return false;
        }
    }

    /**
     * 执行redis脚本语言
     *
     * @param key
     * @param expectValue
     * @param luaScript
     * @return
     */
    public boolean execDelLuaScript(final String key, final String expectValue, final String luaScript) {
        try {
            return execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doAction(Jedis jedis) {
                    // 参数KEY
                    List<String> keys = new ArrayList<>();
                    keys.add(key);
                    // 参数ARGV
                    List<String> args = new ArrayList<>();
                    args.add(expectValue);
                    // 脚本调用g
                    Object result = jedis.eval(luaScript, keys, args);
                    return null != result;
                }
            });
        } catch (JedisConnectionException jce) {
            // 记录异常日志
            // log.error(jce.getMessage(), jce);
            return false;
        }
    }

    /**
     * 功能描述: 在key对应list的尾部添加对象元素,返回队列的长度 并且防止争用情况下出现问题的情况.<br>
     *
     * @param queueName 队列名
     * @param str 添加的元素
     * @return 返回队列的长度
     */
    public Long pushQueue(final String queueName, final String str) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                Long result;
                Long res = jedis.rpushx(queueName, str);
                if (res == 0) {
                    result = jedis.rpush(queueName, str);
                    return result;
                } else {
                    return res;
                }
            }
        });
    }

    /**
     * 功能描述:从队列的头部删除元素，并返回删除元素,该值是以对象的形式存储，队列按先进的先出原则取出
     *
     * @param queueName 队列名
     * @return 取出队列数据 如果队列没有数据返回null
     */
    public String popQueue(final String queueName) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                try {
                    // 执行lpop命令
                    return jedis.lpop(queueName);
                } catch (JedisConnectionException jce) {
                    // 记录异常日志
                    // log.error(jce.getMessage(), jce);
                    return null;
                }
            }
        });
    }

    /**
     * 将给定 key 的值设为 value ，并返回 key 的旧值(old value)。 key 不存在时，返回 null
     *
     * @param key key值
     * @param value 新值
     * @return String 老值
     */
    public String getSet(final String key, final String value) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.getSet(key, value);
            }
        });
    }

    /**
     * key 如果不存在 设置成功返回 true， 否则返回false
     *
     * @param key key值
     * @param value value值
     * @return boolean
     */
    public boolean setnx(final String key, final String value) {
        return execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doAction(Jedis jedis) {
                return jedis.setnx(key, value) == 1;
            }
        });
    }

    /**
     * 功能描述: <br>
     * 执行sequence脚本
     *
     * @param key key值
     * @param fillField 填充域
     * @param luaScript lua脚本
     * @return 返回值
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */

    public Object execSequenceLuaScript(final String key, final String fillField, final String luaScript) {
        return execute(new RedisCallback<Object>() {

            @Override
            public Object doAction(Jedis jedis) {
                try {
                    // 参数KEY
                    List<String> keys = new ArrayList<>();
                    keys.add(key);
                    // 参数ARGV
                    List<String> args = new ArrayList<>();
                    args.add(fillField);
                    // 脚本调用
                    return jedis.eval(luaScript, keys, args);
                } catch (JedisConnectionException jce) {
                    // 记录异常日志
                    // log.error(jce.getMessage(), jce);
                    throw jce;
                }
            }
        });
    }


    /**
     * 存储到set集合
     *
     * @param key
     * @param member
     * @return
     */
    public Long sadd(final String key, final String... member) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.sadd(key, member);
            }
        });
    }

    /**
     * 从set集合随机返回一个元素
     *
     * @param key
     * @param value
     * @return
     */
    public String spop(final String key) {
        return execute(new RedisCallback<String>() {
            @Override
            public String doAction(Jedis jedis) {
                return jedis.spop(key);
            }
        });
    }

    public Long hincrBy(final String key, final String field, final long amount) {
        return execute(new RedisCallback<Long>() {
            @Override
            public Long doAction(Jedis jedis) {
                return jedis.hincrBy(key, field, amount);
            }
        });
    }

    private <T> T execute(RedisCallback<T> command) {
        Jedis jedis = jedisPool.getResource();
        try {
            return command.doAction(jedis);
        } catch (Exception e) {
            throw e;
        } finally {
            jedis.close();
        }
    }

    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
}
